package org.example.mq.brokerserver.core;

import io.micrometer.observation.GlobalObservationConvention;
import org.example.mq.common.MqException;

//实现转发规则的类
//同时负责检验bindingKey是否合法
public class Router {
    public boolean checkBindingKey(String bindingKey){
        if(bindingKey.isEmpty()){
            //在direct和fanout类型中，空字符串是合法的
            return true;
        }
        // bindingKey 的构造规则:
        // 1. 数字, 字母, 下划线
        // 2. 使用 . 分割成若干部分
        // 3. 允许存在 * 和 # 作为通配符. 但是通配符只能作为独立的分段.#:任意一个，*：任意0或者多个
        //检验
        for(int i=0;i<bindingKey.length();i++){
            char ch = bindingKey.charAt(i);
            if(ch >='A' && ch <='Z'){
                continue;
            }
            if (ch >= 'a' && ch <= 'z') {
                continue;
            }
            if (ch >= '0' && ch <= '9') {
                continue;
            }
            if (ch == '_' || ch == '.' || ch == '*' || ch == '#') {
                continue;
            }
            return false;
        }
        //*和#是否为独立部分
        String[] words = bindingKey.split("\\.");
        for(String word:words){
            if(word.length()>1&&(word.contains("*")||word.contains("#"))){
                return false;
            }
        }
        for(int i=0;i<words.length-1;i++){
            //连续的##
            if(words[i].equals("#")&&words[i+1].equals("#")){
                return false;
            }
            //#*
            if(words[i].equals("#")&&words[i+1].equals("*")){
                return false;
            }
            //*#
            if(words[i].equals("*")&&words[i+1].equals("#")){
                return false;
            }
        }
        return true;
    }
    // routingKey 的构造规则:
    // 1. 数字, 字母, 下划线
    // 2. 使用 . 分割成若干部分
    public boolean checkRoutingKey(String routingKey){
        if(routingKey.isEmpty()){
            return true;
        }
        for(int i=0;i<routingKey.length();i++){
            char ch = routingKey.charAt(i);
            if(ch >='A' && ch <='Z'){
                continue;
            }
            if (ch >= 'a' && ch <= 'z') {
                continue;
            }
            if (ch >= '0' && ch <= '9') {
                continue;
            }
            if (ch == '_' || ch == '.') {
                continue;
            }
            return false;
        }
        return true;
    }
    //判断消息是否可以转发找指定的队列(适应于fanout和topic）
    public boolean route(ExchangeType exchangeType,Binding binding,Message message) throws MqException {
        if(exchangeType==ExchangeType.FANOUT){
            return true;
        }else if(exchangeType==ExchangeType.TOPIC){
            //情况复杂，单独写一个方法
            return routeTopic(binding,message);
        }else {
            throw new MqException("[Router] 交换机类型非法！exchangeType"+exchangeType);
        }
    }

    private boolean routeTopic(Binding binding, Message message) {
        String[] routingTokens = message.getRoutingKey().split("\\.");
        String[] bindingTokens = binding.getBindKey().split("\\.");
        int m = routingTokens.length,n = bindingTokens.length;
        //这里使用dp
        // dp[i][j] 表示的含义是 bindingTokens 中的 [0, j] 能否和 routingTokens 中的 [0, i] 匹配.
        boolean[][] dp = new boolean[m+1][n+1];
        //空的bindingKey和空的RoutingKey可以匹配
        dp[0][0] = true;
        //当routingKey为空，bindingKey只有为连续的#才行
        for(int j=1;j<=n;j++){
            if(bindingTokens[j-1].equals("#")){
                dp[0][j]=true;
            }else{
                break;
            }
        }
        //遍历所有情况
        for(int i=1;i<=m;i++){
            for(int j=1;j<=n;j++){
                if(bindingTokens[j-1].equals("#")){
                    dp[i][j] = dp[i-1][j] || dp[i][j-1];
                }else if(bindingTokens[j-1].equals("*")){
                    dp[i][j] = dp[i-1][j-1];
                }else{
                    if(bindingTokens[j-1].equals(routingTokens[i-1])){
                        dp[i][j] = dp[i-1][j-1];
                    }else{
                        dp[i][j] = false;
                    }
                }
            }
        }
        return dp[m][n];
    }

}
